package algo_annotations.data.annotations;

import algo_annotations.data.contrainte_superposition.SegmentFixe;
import algo_annotations.data.sites.Site;
import algo_annotations.data.sites.Site.ConfigCoupureLibelle;
import commons.data.I_Searchable;
import commons.drawing.I_Drawable;
import commons.ihm.ParametresAffichageCarto;
import commons.logs.Logger;
import commons.params_appli.ParamsAppli;
import commons.utils.UtilsCalcul;
import commons.utils.UtilsGeom;
import commons.utils.primitive_geom.Point_d;
import commons.utils.primitive_geom.Rect_d;
import commons.utils.primitive_geom.Segment_d;
import java.awt.Color;
import java.awt.Graphics2D;
import java.util.regex.Pattern;


// La plupart des infos se trouve dans le site de l'anno, qui lui est en 1 seul exemplaire
//Le rect de l'anno depend de la position et de la taille du texte
public class Anno extends Rect_d implements I_Drawable, I_Searchable {

	private enum TypeCalculAmbig {PASS1, PASS2}

	public Site site;
	public LienAnnoSite lienAnnoSite;

	public float geneX, geneY, geneML;

	public double distMinCenterSite;
	public double distMinBordSite;

	public double distCoinCenterSite;
	public double distCoinBordSite;

	public Rect_d rect_protection_superpositionLigneSite;
	public Rect_d rect_protection_superpositionAnno;

	// Mode multiligne
	public boolean bMultiline;
	public String ligne1;
	public String ligne2;
	public Rect_d rect_ligne1;
	public Rect_d rect_ligne2;
	public Rect_d rect_ligne1_protection_superpositionLigneSite;
	public Rect_d rect_ligne2_protection_superpositionLigneSite;
	public Rect_d rect_ligne1_protection_superpositionAnno;
	public Rect_d rect_ligne2_protection_superpositionAnno;

	// VARIABLES POUR OPTIMISATION MEMOIRE
	public Rect_d rect_temp; // utilisé par certaines methodes, evite des "new Rect_d()" gros consomateur de GC
	public Segment_d seg_temp; // idem

	// La note qualifiant le positionnement de l'anno 0=parfait, 1=plus mauvais positionnement possible
	public double noteGlobale;

	// sous-note qualifiant le caractère ambigu ou on de l'anno : on le stocke car on va l'exporter en sortie
	public double noteAmbig;

	double noteAmbig_Simple_passe1;
	
	public Anno(Site site) {
		this.site = site;
		lienAnnoSite = new LienAnnoSite(this);
	    rect_protection_superpositionLigneSite = new Rect_d();
		rect_protection_superpositionAnno = new Rect_d();
		rect_ligne1 = new Rect_d();
		rect_ligne2 = new Rect_d();
		rect_ligne1_protection_superpositionLigneSite = new Rect_d();
		rect_ligne2_protection_superpositionLigneSite = new Rect_d();
		rect_ligne1_protection_superpositionAnno = new Rect_d();
		rect_ligne2_protection_superpositionAnno = new Rect_d();

		rect_temp = new Rect_d();
		seg_temp = new Segment_d();
	}


	// Definit une position normalisee (arguments compris dans [0;1] ) -> reactualise automatiquement les rect
	public void setPosition(float geneX, float geneY, float geneML) {
	    assert(geneX>=0 && geneX<=1) : geneX;
		assert(geneY>=0 && geneY<=1) : geneY;
	    this.geneX = geneX;
		this.geneY = geneY;
		this.geneML = geneML;

	    computeRect(geneX, geneY, geneML);

		// calcul de quelques variables utiles
		distMinCenterSite = UtilsGeom.getDistRectPoint(this, site.x, site.y);
		distMinBordSite = distMinCenterSite - site.rayonSite_reel_m;

		// distance entre le site et le coin de l'anno le plus proche du site
		distCoinCenterSite = UtilsGeom.getDistCoinRectPoint(this, site.x, site.y);
		distCoinBordSite = distCoinCenterSite - site.rayonSite_reel_m;

		updateLienAnnoSite();
	}

	// A appeler à chaque changement de position !
	private void updateLienAnnoSite() {
		lienAnnoSite.updateCoordLien(this);
	}

	//Determine le rectangle de l'annotation, en fonction de sa position par rapport au site et ses dimensions (hauteur, largeur)
	// Doit être rappelé à chaque changement de position de l'anno
	private void computeRect(double x_n, double y_n, double ml_n){
	    
		if (!site.hasAnno()) {
			this.setCoord(site.x, site.y, 0);
			rect_protection_superpositionLigneSite.setCoord(site.x, site.y, 0);
			rect_protection_superpositionAnno.setCoord(site.x, site.y, 0);
			return;
		}

		ConfigCoupureLibelle configCoupureLibelle = site.getConfigCoupureFromGene(ml_n);
		
		if (configCoupureLibelle == null) {
			// MODE SIMPLE LIGNE
			bMultiline = false;

			//on calcule d'abord le centre du rect de l'anno
			// !! on ne calcule pas la position des anno par rapport au rectangle de deplacement max des sites
			//  car pour les annos almostbest, lors de la passe permettant de les améliorer, on recherche une
			//  meilleure position, or leur rectDeplacement est réduit à la position almostBest !! (pour des raisons
			//  de multi-passe permettant d'en trouver le maximum dans la phase de découpage du problème)
//			double xCentre = site.x + 2*(x_n - 0.5) * (site.rectPositionsAnno_reel_m.getWidth()/2 - 0.5*site.largeurAnno_1L_reel_m);
//			double yCentre = site.y + 2*(y_n - 0.5) * (site.rectPositionsAnno_reel_m.getHeight()/2 - 0.5*site.heurAnno_1L_reel_m);
			double xCentre = site.x + 2*(x_n - 0.5) * (site.rayonSite_reel_m + site.distBordSiteMax_reel_m + 0.5*site.largeurAnno_1L_reel_m);
			double yCentre = site.y + 2*(y_n - 0.5) * (site.rayonSite_reel_m + site.distBordSiteMax_reel_m + 0.5*site.hauteurAnno_1L_reel_m);

			setCoord(xCentre - site.largeurAnno_1L_reel_m/2, yCentre - site.hauteurAnno_1L_reel_m/2,
					 xCentre + site.largeurAnno_1L_reel_m/2, yCentre + site.hauteurAnno_1L_reel_m/2);

			rect_protection_superpositionLigneSite.setCoordFrom(this);
			rect_protection_superpositionLigneSite.expand (site.espaceProtectionAnno_superpositionLigneSite_xL_reel_m, site.espaceProtectionAnno_superpositionLigneSite_xL_reel_m);
			rect_protection_superpositionAnno.setCoordFrom(this);
			rect_protection_superpositionAnno.expand (site.espaceProtectionAnno_superpositionAnno_1L_reel_m, site.espaceProtectionAnno_superpositionAnno_1L_reel_m);

		} else {
		
			// MODE MULTILIGNE
			bMultiline = true;

			// tout est précalculé dans la config de coupure
			ligne1 = configCoupureLibelle.ligne1;
			ligne2 = configCoupureLibelle.ligne2;
			double largeurLigne1 = configCoupureLibelle.largeurLigne1_reel_m;
			double largeurLigne2 = configCoupureLibelle.largeurLigne2_reel_m;

			double largeurAnno_multi = Math.max(largeurLigne1, largeurLigne2);
			double hauteurAnno_multi = (2+site.params.facteurEspaceInterligne) * site.hauteurAnno_1L_reel_m;

			double xCentre_multi = site.x + 2*(x_n - 0.5) * (site.rayonSite_reel_m + site.distBordSiteMax_reel_m + 0.5*largeurAnno_multi);
			double yCentre_multi = site.y + 2*(y_n - 0.5) * (site.rayonSite_reel_m + site.distBordSiteMax_reel_m + 0.5*hauteurAnno_multi);

			setCoord(xCentre_multi - largeurAnno_multi/2, yCentre_multi - hauteurAnno_multi/2,
					 xCentre_multi + largeurAnno_multi/2, yCentre_multi + hauteurAnno_multi/2);
			
			// normalement, pas besoin des ces 2 rect en multiligne, mais on pourra l'utiliser
			//  lors du sous decoupage du probleme, pour verifier rapidement si une anno peut intersecter une autre
			//rect_protection_superpositionLigneSite.setCoord(0, 0, 0, 0);
			//rect_protection_superpositionAnno.setCoord(0, 0, 0, 0);
			rect_protection_superpositionLigneSite.setCoordFrom(this);
			rect_protection_superpositionLigneSite.expand (site.espaceProtectionAnno_superpositionLigneSite_xL_reel_m, site.espaceProtectionAnno_superpositionLigneSite_xL_reel_m);
			rect_protection_superpositionAnno.setCoordFrom(this);
			rect_protection_superpositionAnno.expand (site.espaceProtectionAnno_superpositionAnno_2L_reel_m, site.espaceProtectionAnno_superpositionAnno_2L_reel_m);

			// Détermination des rectangles des 2 sous-mots
			if (xMin >= site.x) {
				// Anno completement a droite de son site
				// on aligne les 2 mots à gauche
				rect_ligne1.xMin = xMin;
				rect_ligne1.xMax = xMin + largeurLigne1;
				rect_ligne1.yMax = yMax;
				rect_ligne1.yMin = yMax - site.hauteurAnno_1L_reel_m;

				rect_ligne2.xMin = xMin;
				rect_ligne2.xMax = xMin + largeurLigne2;
				rect_ligne2.yMin = yMin;
				rect_ligne2.yMax = yMin + site.hauteurAnno_1L_reel_m;
			} else if (xMax <= site.x) {
				// Anno completement a gauche de son site
				// on aligne les 2 mots à droite
				rect_ligne1.xMax = xMax;
				rect_ligne1.xMin = xMax - largeurLigne1;
				rect_ligne1.yMax = yMax;
				rect_ligne1.yMin = yMax - site.hauteurAnno_1L_reel_m;

				rect_ligne2.xMax = xMax;
				rect_ligne2.xMin = xMax - largeurLigne2;
				rect_ligne2.yMin = yMin;
				rect_ligne2.yMax = yMin + site.hauteurAnno_1L_reel_m;
			} else {
				// Anno a cheval horizontalement sur son site
				// on tente de centrer les sous rect sur le site
				rect_ligne1.xMin = site.x - largeurLigne1 / 2;
				rect_ligne1.xMax = site.x + largeurLigne1 / 2;
				rect_ligne1.yMax = yMax;
				rect_ligne1.yMin = yMax - site.hauteurAnno_1L_reel_m;
				// si centrage pas possible
				if (rect_ligne1.xMax > xMax) rect_ligne1.decale(xMax - rect_ligne1.xMax, 0);
				if (rect_ligne1.xMin < xMin) rect_ligne1.decale(xMin - rect_ligne1.xMin, 0);

				rect_ligne2.xMin = site.x - largeurLigne2 / 2;
				rect_ligne2.xMax = site.x + largeurLigne2 / 2;
				rect_ligne2.yMin = yMin;
				rect_ligne2.yMax = yMin + site.hauteurAnno_1L_reel_m;
				// si centrage pas possible
				if (rect_ligne2.xMax > xMax) rect_ligne2.decale(xMax - rect_ligne2.xMax, 0);
				if (rect_ligne2.xMin < xMin) rect_ligne2.decale(xMin - rect_ligne2.xMin, 0);
			}
			rect_ligne1_protection_superpositionLigneSite.setCoordFrom(rect_ligne1);
			rect_ligne1_protection_superpositionLigneSite.xMin -= site.espaceProtectionAnno_superpositionLigneSite_xL_reel_m;
			rect_ligne1_protection_superpositionLigneSite.xMax += site.espaceProtectionAnno_superpositionLigneSite_xL_reel_m;
			rect_ligne1_protection_superpositionLigneSite.yMax += site.espaceProtectionAnno_superpositionLigneSite_xL_reel_m;
			rect_ligne1_protection_superpositionLigneSite.yMin = (yMin+yMax)/2; // on s'arrete au milieu du rect global de l'anno

			rect_ligne1_protection_superpositionAnno.setCoordFrom(rect_ligne1);
			rect_ligne1_protection_superpositionAnno.xMin -= site.espaceProtectionAnno_superpositionAnno_2L_reel_m;
			rect_ligne1_protection_superpositionAnno.xMax += site.espaceProtectionAnno_superpositionAnno_2L_reel_m;
			rect_ligne1_protection_superpositionAnno.yMax += site.espaceProtectionAnno_superpositionAnno_2L_reel_m;
			rect_ligne1_protection_superpositionAnno.yMin = (yMin+yMax)/2; // on s'arrete au milieu du rect global de l'anno
			
			
			rect_ligne2_protection_superpositionLigneSite.setCoordFrom(rect_ligne2);
			rect_ligne2_protection_superpositionLigneSite.xMin -= site.espaceProtectionAnno_superpositionLigneSite_xL_reel_m;
			rect_ligne2_protection_superpositionLigneSite.xMax += site.espaceProtectionAnno_superpositionLigneSite_xL_reel_m;
			rect_ligne2_protection_superpositionLigneSite.yMin -= site.espaceProtectionAnno_superpositionLigneSite_xL_reel_m;
			rect_ligne2_protection_superpositionLigneSite.yMax = (yMin+yMax)/2; // on s'arrete au milieu du rect global de l'anno

			rect_ligne2_protection_superpositionAnno.setCoordFrom(rect_ligne2);
			rect_ligne2_protection_superpositionAnno.xMin -= site.espaceProtectionAnno_superpositionAnno_2L_reel_m;
			rect_ligne2_protection_superpositionAnno.xMax += site.espaceProtectionAnno_superpositionAnno_2L_reel_m;
			rect_ligne2_protection_superpositionAnno.yMin -= site.espaceProtectionAnno_superpositionAnno_2L_reel_m;
			rect_ligne2_protection_superpositionAnno.yMax = (yMin+yMax)/2; // on s'arrete au milieu du rect global de l'anno
		}
	}

	public Color getColor() {
		return site.getColor();
	}

	//**************************************************************************
	//*** Calcul de la note qualifiant le PRE-positionnement de l'anno *********
	//**************************************************************************
	public double computeNote_indepAutreAnno(double[] tempData) {
		if (!site.hasAnno())
			return 1;

		double notePreplacement;

		double noteResterSurUneLigne=0;
		double noteDistSonSite=0;
		double noteAlignementHorizontal=0;
		double noteSuperpositionSite=0;
		double noteSuperpositionSegment=0;

		noteResterSurUneLigne = (bMultiline ? 1 : 0);
	    noteDistSonSite = computeNote_DistSonSite();
	    noteAlignementHorizontal = computeNote_AlignementHorizontal();
	    noteSuperpositionSite = computeNote_SuperpositionSite();
		noteSuperpositionSegment = computeNote_IntersectionSegment(tempData);

		notePreplacement = (
				site.params.poids_resterSur1Ligne * noteResterSurUneLigne +
				site.params.poids_noteDistSonSite * noteDistSonSite +
	            site.params.poids_noteAlignementHorizontal * noteAlignementHorizontal +
	            site.params.poids_noteSuperpositionSite * noteSuperpositionSite +
	            site.params.poids_noteSuperpositionSegment * noteSuperpositionSegment)
	            / (site.params.poids_noteDistSonSite + site.params.poids_noteAlignementHorizontal + site.params.poids_noteSuperpositionSite + site.params.poids_noteSuperpositionSegment);

		return notePreplacement;
	}


	//**************************************************************************
	//*** Calcul de la note qualifiant le positionnement de l'anno *************
	//**************************************************************************
	
	// Calcule la note, la stocke dans la variable 'note' et renvoie la valeur
	// La passe 1 de calcul d'ambiguité DOIT avoir été lancée avant !
	public double computeNote_normal(GroupeAnno grpAnno, double[] tempData)	{
		computeNote_normal(grpAnno, tempData, false);
		return noteGlobale;
	}

	// Calcule la note, la stocke dans la variable 'note'
	// Renvoit le détail des note si demandé (bReturnDetailNotes = true)
	// La passe 1 de calcul d'ambiguité DOIT avoir été lancée avant !
	private String computeNote_normal(GroupeAnno grpAnno, double[] tempData, boolean bReturnDetailNotes) {
		if (!site.hasAnno()) {
			noteGlobale=1;
			noteAmbig=1;
			if (bReturnDetailNotes)
				return "<Site sans annotation>";
			else return null;
		}

		// detail des notes
		double noteResterSurUneLigne=0;
		double noteDistSonSite=0;
		double noteAlignementHorizontal=0;
		double noteSuperpositionAutreAnno=0;
		double noteSuperpositionSite=0;
		double noteCroisementLienAnnoSite=0;
		double noteSuperpositionSegment=0;
		double noteAmbiguiteLecture_Anno_Site_MemeCouleur=0;
		double noteAmbiguiteLecture_Site_Anno_MemeCouleur=0;
		double noteAmbiguiteLecture_Anno_Site_AutreCouleur=0;
		double noteAmbiguiteLecture_Site_Anno_AutreCouleur=0;

		//*** ESTHETIQUE : ANNO seule ******************************************
		// Souhait de laisser l'anno sur 1 seule ligne
		noteResterSurUneLigne = (bMultiline ? 1 : 0);

	    //Distance entre l'anno et SON site
	    noteDistSonSite = computeNote_DistSonSite();
	    //Alignement horizontal de l'anno et de son site
	    noteAlignementHorizontal = computeNote_AlignementHorizontal();
	    
	    //*** LISIBILITE : ANNO et LIGNES **************************************
	    //Intersection avec des segments
	    noteSuperpositionSegment = computeNote_IntersectionSegment(tempData);

		//*** LISIBILITE : ANNO et autres ANNO et SITES ************************
	    //Superposition avec d'autres anno
	    noteSuperpositionAutreAnno = computeNote_SuperpositionAutresAnno(grpAnno);
	    //Superposition avec des sites (y compris le sien)
	    noteSuperpositionSite = computeNote_SuperpositionSite();
		//Croisement entre lien anno-site et autres annos
		noteCroisementLienAnnoSite = computeNote_CroisementLienAnnoSite(grpAnno, tempData);

	    //*** AMBIGUITE : Ambiguité par rapport aux autres sites de meme couleur
		// la passe 1 DOIT avoir été lancée avant !
	    noteAmbiguiteLecture_Anno_Site_MemeCouleur = computeNote_AmbiguiteLecture_Anno_Site(grpAnno, true, TypeCalculAmbig.PASS2);
		noteAmbiguiteLecture_Site_Anno_MemeCouleur = computeNote_AmbiguiteLecture_Site_Anno(grpAnno, true, TypeCalculAmbig.PASS2);

	    // Ambiguité par rapport aux autres sites de couleurs différentes
		if (site.params.bUseDifferentiationCouleur) {
			noteAmbiguiteLecture_Anno_Site_AutreCouleur = computeNote_AmbiguiteLecture_Anno_Site(grpAnno, false, TypeCalculAmbig.PASS2);
			noteAmbiguiteLecture_Site_Anno_AutreCouleur = computeNote_AmbiguiteLecture_Site_Anno(grpAnno, false, TypeCalculAmbig.PASS2);
		}

	    noteGlobale = (site.params.poids_resterSur1Ligne * noteResterSurUneLigne +
				site.params.poids_noteDistSonSite * noteDistSonSite +
	            site.params.poids_noteAlignementHorizontal * noteAlignementHorizontal +
	            site.params.poids_noteAmbiguiteLecture_MemeCouleur * (noteAmbiguiteLecture_Anno_Site_MemeCouleur + noteAmbiguiteLecture_Site_Anno_MemeCouleur)/2 +
	            site.params.poids_noteAmbiguiteLecture_AutreCouleur * (noteAmbiguiteLecture_Anno_Site_AutreCouleur + noteAmbiguiteLecture_Site_Anno_AutreCouleur)/2 +
	            site.params.poids_noteSuperpositionAutreAnno * noteSuperpositionAutreAnno +
	            site.params.poids_noteSuperpositionSite * noteSuperpositionSite +
	            site.params.poids_noteSuperpositionSegment * noteSuperpositionSegment +
				site.params.poids_noteCroisementLienAnnoSite * noteCroisementLienAnnoSite)
	            / site.params.poids_TOTAL;

		// On stocke cette partie de la note car elle sera exportée dans les fichiers de sortie
		noteAmbig = (site.params.poids_noteAmbiguiteLecture_MemeCouleur * (noteAmbiguiteLecture_Anno_Site_MemeCouleur + noteAmbiguiteLecture_Site_Anno_MemeCouleur)/2 +
	                site.params.poids_noteAmbiguiteLecture_AutreCouleur * (noteAmbiguiteLecture_Anno_Site_AutreCouleur + noteAmbiguiteLecture_Site_Anno_AutreCouleur)/2
					) / (site.params.poids_noteAmbiguiteLecture_MemeCouleur + site.params.poids_noteAmbiguiteLecture_AutreCouleur);


		if (bReturnDetailNotes)
			// Creation d'une chaine resumant le detail des notes
			return
					"Sur une ligne ?       : " + noteResterSurUneLigne + "\n" + 
					"Dist. son site        : " + UtilsCalcul.round(noteDistSonSite, 3) + "\n" +
					"Align. horizont       : " + UtilsCalcul.round (noteAlignementHorizontal, 3) + "\n" +

					"Superposit° annos     : " + UtilsCalcul.round (noteSuperpositionAutreAnno, 3) + "\n" +
					"Superposit° site      : " + UtilsCalcul.round (noteSuperpositionSite, 3) + "\n" +
					"Intersect° ligne      : " + UtilsCalcul.round (noteSuperpositionSegment, 3) + "\n" +
					"Croisement lien       : " + UtilsCalcul.round (noteCroisementLienAnnoSite, 3) + "\n" +

					"Ambig. A->S meme coul : " + UtilsCalcul.round (noteAmbiguiteLecture_Anno_Site_MemeCouleur, 3) + "\n" +
					"Ambig. S->A meme coul : " + UtilsCalcul.round (noteAmbiguiteLecture_Site_Anno_MemeCouleur, 3) + "\n" +
					"Ambig. A->S autre coul: " + UtilsCalcul.round (noteAmbiguiteLecture_Anno_Site_AutreCouleur, 3) + "\n" +
					"Ambig. S->A autre coul: " + UtilsCalcul.round (noteAmbiguiteLecture_Site_Anno_AutreCouleur, 3) + "\n" +
					" (ambig simple passe1 : " + UtilsCalcul.round (noteAmbig_Simple_passe1, 3) + ")\n" +

					" => GLOBALE      : " + UtilsCalcul.round (noteGlobale, 3) + "\n";
		else
			return null;

	}

	// A lancer sur toutes les anno avant de lancer le calcul normal
	public void precomputeAmbiguiteSimple_passe1(GroupeAnno grpAnno) {
		double noteAmbiguiteLecture_Anno_Site_MemeCouleur=0;
		double noteAmbiguiteLecture_Site_Anno_MemeCouleur=0;
		double noteAmbiguiteLecture_Anno_Site_AutreCouleur=0;
		double noteAmbiguiteLecture_Site_Anno_AutreCouleur=0;

		noteAmbiguiteLecture_Anno_Site_MemeCouleur = computeNote_AmbiguiteLecture_Anno_Site(grpAnno, true, TypeCalculAmbig.PASS1);
		noteAmbiguiteLecture_Site_Anno_MemeCouleur = computeNote_AmbiguiteLecture_Site_Anno(grpAnno, true, TypeCalculAmbig.PASS1);

	    //Ambiguité par rapport aux autres sites de couleur différent
		if (site.params.bUseDifferentiationCouleur) {
			noteAmbiguiteLecture_Anno_Site_AutreCouleur = computeNote_AmbiguiteLecture_Anno_Site(grpAnno, false, TypeCalculAmbig.PASS1);
			noteAmbiguiteLecture_Site_Anno_AutreCouleur = computeNote_AmbiguiteLecture_Site_Anno(grpAnno, false, TypeCalculAmbig.PASS1);
		}
		noteAmbig_Simple_passe1 =
				(
	            site.params.poids_noteAmbiguiteLecture_MemeCouleur * (noteAmbiguiteLecture_Anno_Site_MemeCouleur + noteAmbiguiteLecture_Site_Anno_MemeCouleur)/2 +
	            site.params.poids_noteAmbiguiteLecture_AutreCouleur * (noteAmbiguiteLecture_Anno_Site_AutreCouleur + noteAmbiguiteLecture_Site_Anno_AutreCouleur)/2
				) / (site.params.poids_noteAmbiguiteLecture_MemeCouleur + site.params.poids_noteAmbiguiteLecture_AutreCouleur);
	}


	// Note qualifiant la distance entre l'annotation et SON site
	private double computeNote_DistSonSite() {
		if (distMinBordSite > site.distBordSiteMax_reel_m)
			return 1; //possible si l'anno est dans le coin du rectMax du site
		else if(distMinBordSite > site.distBordSiteIdeale_reel_m)
			return (distMinBordSite - site.distBordSiteIdeale_reel_m) / (site.distBordSiteMax_reel_m - site.distBordSiteIdeale_reel_m);
		else
			return (site.distBordSiteIdeale_reel_m - distMinBordSite) / (site.distBordSiteIdeale_reel_m+site.rayonSite_reel_m);
	}


	// Note qualifiant l'alignement horizontal de l'annotation avec son site
	private double computeNote_AlignementHorizontal() {
	    //on se ramene à un angle compris entre 0 (note=0) et Pi/2 (note=1)
		double angle_temp = lienAnnoSite.angle;
	    //d'abord entre 0 et Pi
	    if (angle_temp < 0)  angle_temp = -angle_temp;
	    //puis entre 0 et 0.25
	    if (angle_temp > Math.PI/2) angle_temp = Math.PI - angle_temp;
	    
		assert(angle_temp>=0 && angle_temp<=Math.PI/2) : angle_temp;

		//calcul de la note
	    return angle_temp * 2/Math.PI;
	}


	//Note qualifiant la superposition avec d'autres anno
	private double computeNote_SuperpositionAutresAnno(GroupeAnno grpAnno) {
		if (bMultiline) {
			double note_temp = computeNote_SuperpositionAutresAnno(grpAnno, rect_ligne1_protection_superpositionAnno);
			note_temp += computeNote_SuperpositionAutresAnno(grpAnno, rect_ligne2_protection_superpositionAnno);
			return Math.tanh(note_temp);

		} else
			return Math.tanh(computeNote_SuperpositionAutresAnno(grpAnno, rect_protection_superpositionAnno));
	}
	// rect sera this pour une anno simple ligne ou successivement 2 sous-rect si multiligne
	private double computeNote_SuperpositionAutresAnno(GroupeAnno grpAnno, Rect_d rect) {
		// au dela de cette valeur, la note vaut 1;
		double maxSurfaceIntersect = 10 * site.hauteurAnno_1L_reel_m*site.hauteurAnno_1L_reel_m;
		double accuSurfaceIntersect = 0;

		// détermination d'un éventuel sous-ensemble de segments
		Site[] tabSite = site.tab_tabSiteIntersectAnno[getSousRect(rect)];
		int nbSite = tabSite.length;
	    for (int i=0 ; i<nbSite ; i++) {
			Site autreSite = tabSite[i];
			if (!site.hasAnno()) continue; // c'est un site sans annotation
			Anno annoAutreSite = grpAnno.vAnno.get(autreSite.index);

			if (annoAutreSite.bMultiline) {
				accuSurfaceIntersect += UtilsGeom.getSurfaceRectIntersection(rect, annoAutreSite.rect_ligne1_protection_superpositionAnno);
				accuSurfaceIntersect += UtilsGeom.getSurfaceRectIntersection(rect, annoAutreSite.rect_ligne2_protection_superpositionAnno);
			} else {
				accuSurfaceIntersect += UtilsGeom.getSurfaceRectIntersection(rect, annoAutreSite.rect_protection_superpositionAnno);
			}
	    }

		return accuSurfaceIntersect / maxSurfaceIntersect;
	}


	//Note qualifiant la superposition de l'anno avec des sites (y compris le sien)
	private double computeNote_SuperpositionSite() {
		if (bMultiline) {
			double note_temp = computeNote_SuperpositionSite(rect_ligne1_protection_superpositionLigneSite);
			note_temp += computeNote_SuperpositionSite(rect_ligne2_protection_superpositionLigneSite);
			if (note_temp>1)
				return 1;
			else
				return note_temp;

		} else
			return computeNote_SuperpositionSite(rect_protection_superpositionLigneSite);
	}
	// rect sera this pour une anno simple ligne ou successivement 2 sous-rect si multiligne
	private double computeNote_SuperpositionSite(Rect_d rect) {
	    double distCenterSite;
	    double n=0;

		int nbsite = site.tabSiteSuperposables.length;
		for (int i=0 ; i<nbsite ; i++) {
			Site s = site.tabSiteSuperposables[i];
			double rayon = s.rayonSite_reel_m;

			// petit test pour virer la plupart des cas où le site ne sra pas superposé à l'anno
			if (s.x+rayon <= rect.xMin || s.x-rayon >= rect.xMax ||
				s.y+rayon <= rect.yMin || s.y-rayon >= rect.yMax)
				continue;

			distCenterSite = UtilsGeom.getDistRectPoint (rect, s.x, s.y);
			if (distCenterSite < rayon)
				n += 0.3333 * (rayon - distCenterSite) / rayon;
			if (n>=1)
				return 1;
		}
	    return n;
	}

	
	private double computeNote_IntersectionSegment(double[] tempData) {
		if (bMultiline) {
			double note_temp = computeNote_IntersectionSegment(rect_ligne1_protection_superpositionLigneSite, tempData);
			note_temp += computeNote_IntersectionSegment(rect_ligne2_protection_superpositionLigneSite, tempData);
			if (note_temp>1)
				return 1;
			else
				return note_temp;

		} else
			return computeNote_IntersectionSegment(rect_protection_superpositionLigneSite, tempData);
	}

	private double computeNote_IntersectionSegment(Rect_d rect, double[] tempData) {
	    double lgMaxIntersect = 5 * site.hauteurAnno_1L_reel_m;
		double lgIntersect=0;

		// rectangle de l'anno, grossi de la largeur du segment en cours
		Rect_d rect_larglarg = rect_temp;
		double largeurSegCourante = -1;

		// détermination d'un éventuel sous-ensemble de segments
		SegmentFixe[] tabSegment = site.tab_tabSegmentIntersectable[getSousRect(rect)];
		int nbSeg = tabSegment.length;
		for (int i=0 ; i<nbSeg ; i++) {
			SegmentFixe seg = tabSegment[i];

			if (largeurSegCourante != seg.getLargeurSeg_reel_m()) {
				// TODO_OK PERF : ne faire ca que si changement de largeur de segment !
				rect_larglarg.setCoordFrom(rect);
				rect_larglarg.expand(seg.getLargeurSeg_reel_m()/2, seg.getLargeurSeg_reel_m()/2);
				largeurSegCourante = seg.getLargeurSeg_reel_m();
			}

			// on fait un pretest rapide de bounding box
			// ce test sera refait dans computeLgIntersect_Rect_Segment(), mais on suppose
			// que ce premier test va éliminer le segment très rapidement
			// ce test est plus rapide car on utilise le fait que SegmentFixe est fixe
			//  (on dispose des variables précalculées xMin, xMax, etc.)
			if (seg.xMin>=rect_larglarg.xMax || seg.xMax<=rect_larglarg.xMin ||
				seg.yMin>=rect_larglarg.yMax || seg.yMax<=rect_larglarg.yMin)
				continue;

	        //lgIntersect += UtilsGeom.computeLgIntersect_Rect_Segment(rect_larglarg, seg);
			lgIntersect += UtilsGeom.computeLgIntersect_Rect_Segment__fast(rect_larglarg, seg, tempData);
			if (lgIntersect>=lgMaxIntersect)
				return 1;
	    }
	    return lgIntersect / lgMaxIntersect;
	}



	private double computeNote_CroisementLienAnnoSite(GroupeAnno grpAnno, double[] tempData) {
		double n = 0;

		// détermination d'un éventuel sous-ensemble de segments
		Site[] tabSite = site.tab_tabSiteIntersectAnno[getSousRect(this)];
		int nbSite = tabSite.length;
		for (int i=0 ; i<nbSite ; i++) {
			Site autreSite = tabSite[i];
			if (!autreSite.hasAnno()) continue; // c'est un site sans annotation
			Anno annoAutreSite = grpAnno.vAnno.get(autreSite.index);

			// on fait un pretest rapide de bounding box
			// ce test sera refait dans computeLgIntersect_Rect_Segment(), mais on suppose
			// que ce premier test va éliminer le segment très rapidement et éviter un appel à computeLgIntersect...
			if ( (lienAnnoSite.pt1.x>=annoAutreSite.xMax && lienAnnoSite.pt2.x>=annoAutreSite.xMax) ||
				 (lienAnnoSite.pt1.x<=annoAutreSite.xMin && lienAnnoSite.pt2.x<=annoAutreSite.xMin) ||
				 (lienAnnoSite.pt1.y>=annoAutreSite.yMax && lienAnnoSite.pt2.y>=annoAutreSite.yMax) ||
				 (lienAnnoSite.pt1.y<=annoAutreSite.yMin && lienAnnoSite.pt2.y<=annoAutreSite.yMin) )
				continue;

			//Segment_d seg = UtilsGeom.computeSegIntersect_Rect_Segment(annoAutreSite, this.lienAnnoSite);
			Segment_d seg = UtilsGeom.computeSegIntersect_Rect_Segment__fast(annoAutreSite, this.lienAnnoSite, tempData, seg_temp);
			if (seg != null)
				//n += 0.333 * (1 - Math.abs((seg.getCenter().x - annoAutreSite.getCenter().x)) / (annoAutreSite.getWidth()/2) );
				n += 0.333 * (1 - Math.abs(( (seg.pt1.x+seg.pt2.x)/2 - (annoAutreSite.xMin+annoAutreSite.xMax)/2 )) / (annoAutreSite.getWidth()/2) );
			if (n>=1)
				return 1;
	    }
		return n;
	}


	// A partir d'une anno, est-ce facile de retrouver visuellement son site ?
	private double computeNote_AmbiguiteLecture_Anno_Site(GroupeAnno grpAnno, boolean bMemeCouleur, TypeCalculAmbig typeCalculAmbig) {
	    double n=0;

		//distance de l'anno a son propre site
	    double distHybrideBordSite = ParamsAppli.ANNO_DISTHYBRIDE_FACT_BORD*distMinBordSite + ParamsAppli.ANNO_DISTHYBRIDE_FACT_COIN*distCoinBordSite;

	    //evite des notes d'ambiguite anormalement faible si distSite proche de 0
		// sans ca, les anno vont se coller comme des sangsue à leur site ! ...
	    if (distHybrideBordSite < site.distBordSiteIdeale_reel_m / 3)
	    	distHybrideBordSite = site.distBordSiteIdeale_reel_m / 3;

	    double distAutreSite;

		Site[] tabSite;

		// Choix des sites proches, suivant la proximité de l'anno a son site
		if (distMinBordSite <= site.distBordSiteMax_reel_m*site.factorSmall) {
			if (bMemeCouleur) tabSite = site.tabSiteAmbiguiteSmall_Anno_Site_memeCouleur;
			else tabSite = site.tabSiteAmbiguiteSmall_Anno_Site_autreCouleur;
		} else if (distMinBordSite <= site.distBordSiteMax_reel_m*site.factorMedium) {
			if (bMemeCouleur) tabSite = site.tabSiteAmbiguiteMedium_Anno_Site_memeCouleur;
			else tabSite = site.tabSiteAmbiguiteMedium_Anno_Site_autreCouleur;
		} else {
			if (bMemeCouleur) tabSite = site.tabSiteAmbiguiteMax_Anno_Site_memeCouleur;
			else tabSite = site.tabSiteAmbiguiteMax_Anno_Site_autreCouleur;
		}

		int nbSite = tabSite.length;
	    for (int i=0 ; i<nbSite ; i++) {
			Site autreSite = tabSite[i];
	        distAutreSite = getDistHybrideBordOtherSite(autreSite);

			// Amélioration du calcul d'ambiguité (2 passes)
			double factorPasse2 = 1;
			if (typeCalculAmbig == TypeCalculAmbig.PASS2) {
				// une 1ere passe a ete faite pour un 1er calcul simple d'ambiguite
				// si autreSite a cette note simple tres faible, alors cela
				// a un impact positif sur l'ambiguite qu'il peut avoir avec this

				if (!autreSite.hasAnno()) // c'est un site sans annotation
					factorPasse2 = 1; // pas d'impact positif possible de autreSite
				else {
					Anno annoAutreSite = grpAnno.vAnno.get(autreSite.index);
					if (annoAutreSite.noteAmbig_Simple_passe1 <= 0.3)
						//factorPasse2 = annoAutreSite.noteAmbig_Simple_passe1 + 0.4; // le facteur ne doit pas etre trop petit qd meme : minimum 0.4
						factorPasse2 = (1 - 0.4)/0.3 * annoAutreSite.noteAmbig_Simple_passe1 + 0.4;
					else
						factorPasse2 = 1; // autreSite présente lui aussi certaine ambiguité (simple, de la passe1), il ne peut pas avoir un effet positif
				}
			} else if (typeCalculAmbig == TypeCalculAmbig.PASS1)
				// On est en passe1, on se contente d'un calcul simple
				factorPasse2 = 1;
			else
				assert(false);


			// n peut exploser avec ces calculs, TANH est la pour normaliser tout ca
			n += getNoteAmbiguiteUnitaire (distHybrideBordSite, distAutreSite) * factorPasse2;
			if (n>4) break; // le resultat sera > 0.9999 = 1
		}
		return Math.tanh(n);
	}

	// A partir d'un site, est-ce facile de retrouver visuellement son anno ?
	private double computeNote_AmbiguiteLecture_Site_Anno(GroupeAnno grpAnno, boolean bMemeCouleur, TypeCalculAmbig typeCalculAmbig) {
	    double n=0;

		//distance du site à sa propre anno
	    double distHybrideBordSite_sonAnno = ParamsAppli.ANNO_DISTHYBRIDE_FACT_BORD*distMinBordSite + ParamsAppli.ANNO_DISTHYBRIDE_FACT_COIN*distCoinBordSite;

	    //evite des notes d'ambiguite anormalement faible si distSite proche de 0
		// sans ca, les anno vont se coller comme des sangsue à leur site ! ...
	    if (distHybrideBordSite_sonAnno < site.distBordSiteIdeale_reel_m / 3)
	    	distHybrideBordSite_sonAnno = site.distBordSiteIdeale_reel_m / 3;

	    double distHybrideBordSite_autreAnno;

		Site[] tabSite;

		// Choix des sites proches, suivant la proximité de l'anno a son site
		if (distMinBordSite <= site.distBordSiteMax_reel_m*site.factorSmall) {
			if (bMemeCouleur) tabSite = site.tabSiteAmbiguiteSmall_Site_Anno_memeCouleur;
			else tabSite = site.tabSiteAmbiguiteSmall_Site_Anno_autreCouleur;
		}  else if (distMinBordSite <= site.distBordSiteMax_reel_m*site.factorMedium) {
			if (bMemeCouleur) tabSite = site.tabSiteAmbiguiteMedium_Site_Anno_memeCouleur;
			else tabSite = site.tabSiteAmbiguiteMedium_Site_Anno_autreCouleur;
		} else {
			if (bMemeCouleur) tabSite = site.tabSiteAmbiguiteMax_Site_Anno_memeCouleur;
			else tabSite = site.tabSiteAmbiguiteMax_Site_Anno_autreCouleur;
		}

		// parcours des autres anno potentiellement ambigues
		int nbSite = tabSite.length;
	    for (int i=0 ; i<nbSite ; i++) {
			Site autreSite = tabSite[i];
			if (!autreSite.hasAnno()) continue; // c'est un site sans annotation

			Anno annoAutreSite = grpAnno.vAnno.get(autreSite.index);
			distHybrideBordSite_autreAnno = annoAutreSite.getDistHybrideBordOtherSite(this.site);

			// Amélioration du calcul d'ambiguité (2 passes)
			double factorPasse2 = 1;
			if (typeCalculAmbig == TypeCalculAmbig.PASS2) {
				// une 1ere passe a ete faite pour un 1er calcul simple d'ambiguite
				// si autreSite a cette note simple tres faible, alors cela
				// a un impact positif sur l'ambiguite qu'il peut avoir avec this
				if (annoAutreSite.noteAmbig_Simple_passe1 <= 0.3)
					//factorPasse2 = annoAutreSite.noteAmbig_Simple_passe1 + 0.4; // le facteur ne doit pas etre trop petit qd meme : minimum 0.4
					factorPasse2 = (1 - 0.4)/0.3 * annoAutreSite.noteAmbig_Simple_passe1 + 0.4;
				else
					factorPasse2 = 1; // autreSite présente lui aussi une certaine ambiguité (simple, de la passe1), il ne peut pas avoir un effet positif

			} else if (typeCalculAmbig == TypeCalculAmbig.PASS1)
				// On est en passe1, on se contente d'un calcul simple
				factorPasse2 = 1;
			else
				assert(false);


			// n peut exploser avec ces calculs, TANH est la pour normaliser tout ca
			n += getNoteAmbiguiteUnitaire (distHybrideBordSite_sonAnno, distHybrideBordSite_autreAnno) * factorPasse2;
			if (n>4) break; // le resultat sera > 0.9999 = 1
		}
		return Math.tanh(n);
	}

	
	private double getNoteAmbiguiteUnitaire(double distPropre, double distAutreObjet) {
		if (distAutreObjet >= site.params.facteurLimiteAmbiguite * distPropre)
			return 0; // pas d'ambiguite

		//cas particuliers
		if (distAutreObjet<=0 && distPropre<=0)
			return 0.3;
		else if (distAutreObjet<=0 && distPropre>0)
			return 1;
		else

			return 0.3 * distPropre / distAutreObjet;
	}


	// rect sera this pour une anno simple ligne ou successivement 2 sous-rect si multiligne
	private int getSousRect(Rect_d rect) {
		if (rect.xMin >= site.x) {
			// RIGHT
			if (rect.yMin >= site.y)
				return Site.SSRECT_UPRIGHT;
			else if (rect.yMax <= site.y)
				return Site.SSRECT_DOWNRIGHT;
			else
				return Site.SSRECT_RIGHT;
		} else if (rect.xMax <= site.x) {
			// LEFT
			if (rect.yMin >= site.y)
				return Site.SSRECT_UPLEFT;
			else if (rect.yMax <= site.y)
				return Site.SSRECT_DOWNLEFT;
			else
				return Site.SSRECT_LEFT;
		} else {
			// milieu horizontalement : reste a tester si UP ou DOWN
			if (rect.yMin >= site.y)
				return Site.SSRECT_UP;
			else if (rect.yMax <= site.y)
				return Site.SSRECT_DOWN;
			else
				// optim impossible vu le placement de l'anno (genre placée au dessus de son site ...)
				return Site.SSRECT_FULL;
		}
	}

	public double getDistMinBordOtherSite(Site s) {
		return UtilsGeom.getDistRectPoint(this, s.x, s.y) - s.rayonSite_reel_m;
	}
	public double getDistHybrideBordOtherSite(Site s) {
		double distMinBord = UtilsGeom.getDistRectPoint(this, s.x, s.y) - s.rayonSite_reel_m;
		double distCoinBord = UtilsGeom.getDistCoinRectPoint(this, s.x, s.y) - s.rayonSite_reel_m;
		return ParamsAppli.ANNO_DISTHYBRIDE_FACT_BORD*distMinBord + ParamsAppli.ANNO_DISTHYBRIDE_FACT_COIN*distCoinBord;
	}

	//****** IDrawable *********************************************************

	@Override
	public void draw(Graphics2D g, ParametresAffichageCarto pac) {
		if (!site.hasAnno()) return; // site sans anno

		g.setColor(pac.getColorSelonModeAffichage(site.getColor()));
	
		g.setStroke(pac.getBasicStroke1());
		site.data.params.myFont.setSize(pac.lg2LG(site.hauteurAnno_1L_reel_m));
		g.setFont(site.data.params.myFont.getFont());

		if (bMultiline) {
			g.drawString(ligne1, pac.x2X(rect_ligne1.xMin), pac.y2Y(rect_ligne1.yMin));
			g.drawString(ligne2, pac.x2X(rect_ligne2.xMin), pac.y2Y(rect_ligne2.yMin));
		} else {
			g.drawString(site.libelle, pac.x2X(xMin), pac.y2Y(yMin));
		}
	}

	@Override
	public String toString() {
		return getInfoBulle();
	}

	@Override
	public String getInfoBulle() {
		return site.getInfoBulle();
	}

	@Override
	public String getInfoDetaillee() {
		if (!site.hasAnno()) return "";
		
		// On affiche le détail des notes
		// On recalcule la note pour l'occasion : il nous faut le groupe d'anno de cette anno
		//  or si on affiche les infos détaillées, c'est que l'anno fait partie du meilleur
		//  groupe d'anno, on va donc demander au Data_a le meilleur groupe d'anno

		// avant le calcul normal de la note, on doit lancer un precalcul d'ambiguite simple sur les anno alentour (toutes, en fait pour plus de simplicité, car rapide)
		site.data.getBestGroupeAnno().precomputeAbiguiteSimple_passe1(null, null);

		String res = computeNote_normal(site.data.getBestGroupeAnno(), new double[4], true) + "\n" +
				     site.getInfoDetaillee() + "\n";


		if (Logger.isDebugLevel()) {
			res += "Optim ALSA (même couleur) :\n" +
					" - nbSite Max : " + site.tabSiteAmbiguiteMax_Site_Anno_memeCouleur.length + "\n" +
					" - nbSite Med : " + site.tabSiteAmbiguiteMedium_Site_Anno_memeCouleur.length + "\n" +
					" - nbSite Sml : " + site.tabSiteAmbiguiteSmall_Site_Anno_memeCouleur.length + "\n" +
					"Optim ALAS (même couleur) :\n" +
					" - nbSite Max : " + site.tabSiteAmbiguiteMax_Anno_Site_memeCouleur.length + "\n" +
					" - nbSite Med : " + site.tabSiteAmbiguiteMedium_Anno_Site_memeCouleur.length + "\n" +
					" - nbSite Sml : " + site.tabSiteAmbiguiteSmall_Anno_Site_memeCouleur.length + "\n";
		}

		return res;
	}


	@Override
	public double distTo(Point_d pt) {
		return UtilsGeom.getDistRectPoint(this, pt.x, pt.y);
	}

	@Override
	public boolean matchSearch(Pattern pattern) {
		return site.matchSearch(pattern);
	}
	
	
}
